package com.ssrs.framework.extend.plugin;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import com.ssrs.framework.FrameworkPlugin;
import com.ssrs.framework.extend.annotation.Plugin;

import java.util.*;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author ssrs
 */
public class PluginManager {

    private static final Log log = LogFactory.get(PluginManager.class);
    private ArrayList<PluginConfig> configList = null;
    private ReentrantLock lock = new ReentrantLock();
    private static PluginManager instance = new PluginManager();

    public static PluginManager getInstance() {
        return instance;
    }

    public void init() {
        if (configList == null || configList.size() == 0) {
            lock.lock();
            try {
                if (configList == null || configList.size() == 0) {
                    loadAllConfig();
                }
            } finally {
                lock.unlock();
            }
        }
    }

    public void loadAllConfig() {
        configList = new ArrayList<PluginConfig>();
        Map<String, PluginConfig> map = new LinkedHashMap<>();
        // 首先加载framework插件
        try {
            PluginConfig pc = new PluginConfig();
            pc.parse(FrameworkPlugin.class);
            map.put(pc.getId(), pc);
        } catch (PluginException e) {
            e.printStackTrace();
        }


        // 加载其他插件

        String packageName = ClassUtil.getPackage(FrameworkPlugin.class);
        int packageEndIndex = packageName.lastIndexOf(StrUtil.DOT);
        if (packageEndIndex != -1) {
            packageName = packageName.substring(0, packageEndIndex);
        }

        try {
            Set<Class<?>> pluginClasses = ClassUtil.scanPackage(packageName, aClass -> aClass.isAnnotationPresent(Plugin.class));
            if (CollUtil.isNotEmpty(pluginClasses)) {
                for (Class<?> pluginClass : pluginClasses) {
                    PluginConfig pc = new PluginConfig();
                    pc.parse(pluginClass);
                    if (map.containsKey(pc.getId()) && !pc.getId().equals(FrameworkPlugin.class.getName())) {
                        log.warn("PluginConfig is overrode:" + pc.getId());
                    }
                    if (!pc.getId().equals(FrameworkPlugin.class.getName())) {
                        map.put(pc.getId(), pc);
                    }

                }

            }
        } catch (PluginException e) {
            e.printStackTrace();
        }
        configList.addAll(map.values());

        ArrayList<PluginConfig> tempEachList = new ArrayList(this.configList);
        ArrayList<PluginConfig> result = new ArrayList();
        Iterator iterator1 = tempEachList.iterator();

        while (iterator1.hasNext()) {
            PluginConfig pc = (PluginConfig) iterator1.next();
            this.sort(result, pc);
        }

        this.configList = new ArrayList<>(result);
    }

    private void sort(List<PluginConfig> result, PluginConfig pc) {
        if (this.getPluginConfig(result, pc.getId()) == null) {
            this.configList.remove(pc);
            Iterator iterator = pc.getRequiredPlugins().keySet().iterator();

            while (iterator.hasNext()) {
                String pluginId = (String) iterator.next();
                PluginConfig c = this.getPluginConfig(this.configList, pluginId);
                if (c != null) {
                    this.sort(result, c);
                }
            }

            if (this.getPluginConfig(result, pc.getId()) == null) {
                result.add(pc);
            }

        }
    }

    public PluginConfig getPluginConfig(List<PluginConfig> list, String pluginId) {
        for (PluginConfig c : list) {
            if (c.getId().equals(pluginId)) {
                return c;
            }
        }
        return null;
    }

    public PluginConfig getPluginConfig(String pluginId) {
        return getPluginConfig(configList, pluginId);
    }

    public ArrayList<PluginConfig> getAllPluginConfig() {
        return configList;
    }

    public void destory() {
        configList = null;
    }
}
